/*****************************************************************************
 *   Copyright(C)2009-2022 by VSF Team                                       *
 *                                                                           *
 *  Licensed under the Apache License, Version 2.0 (the "License");          *
 *  you may not use this file except in compliance with the License.         *
 *  You may obtain a copy of the License at                                  *
 *                                                                           *
 *     http://www.apache.org/licenses/LICENSE-2.0                            *
 *                                                                           *
 *  Unless required by applicable law or agreed to in writing, software      *
 *  distributed under the License is distributed on an "AS IS" BASIS,        *
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. *
 *  See the License for the specific language governing permissions and      *
 *  limitations under the License.                                           *
 *                                                                           *
 ****************************************************************************/

/*============================ INCLUDES ======================================*/

#include "component/ui/vsf_ui_cfg.h"

#if VSF_USE_UI == ENABLED && VSF_DISP_USE_VGA_M480 == ENABLED

#define __VSF_EDA_CLASS_INHERIT__
#define __VSF_DISP_CLASS_INHERIT__
#define __VSF_DISP_VGA_M480_CLASS_IMPLEMENT

#include "kernel/vsf_kernel.h"
#include "../../../vsf_disp.h"
#include "./vsf_disp_vga_m480.h"

/* VGA implementation by M480 PDMA

resources used:
PB0-7, PB12 - PB15
PDMA CH8-CH15
TIMER0, TIMER1, TIMER2
EPWM1

clock configurations:
#define M480_PLL_FREQ_HZ                (288 * 1000 * 1000)
#define M480_HCLK_FREQ_HZ               (288 * 1000 * 1000)
#define M480_HXT_FREQ_HZ                (12 * 1000 * 1000)
#define M480_PCLK0_FREQ_HZ              (72 * 1000 * 1000)
#define M480_PCLK1_FREQ_HZ              (72 * 1000 * 1000)

set to below and reduce the resolution if not stable:
#define M480_PLL_FREQ_HZ                (144 * 1000 * 1000)
#define M480_HCLK_FREQ_HZ               (144 * 1000 * 1000)


notation:
pixel_clk:      VGA Pixel Clock
div_pixel_clk:  pixel_clock / n, n = [1 - 4]

waveform(n = 2):
div_pixel_clk(TIMER_PIXEL triggers PDMA_PIXEL)  : trigger pixel output
hsync(unit pixel, PWM_HSYNC)                    : hsync of VGA, generated by PWM
hsync_trg(TIMER_HSYNC triggered by TIMER_PIXEL) : trigger pixel output enable

div_pixel_clk:    _   _   _   _   _   _   _   _   _   _   _   _   _   _   _   _   _   _   _ 
                _| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_| |_
                 |       |     |                               |       |     |
hsync:           |       |     |                               |       |     |
                _|       |_____|_______________________________|       |_____|____
                 |_HSYNC_|     |                               |_HSYNC_|     |
hsync_trg:       |             |                               |             |
                 |_____________|                               |_____________|
                _| HSYNC+FPORCH|__________LINE_ACTIVE__________| HSYNC+FPORCH|____
                 |             |-> PIXEL_ACTIVE <-|-> BPORCH <-|             |
                 |-> state3    |-> state0                      |-> state1    |-> state 2
PDMA_HSYNC scatter-gather chain:
init:
    TIMER_HSYNC->CMP    = HSYNC + PORCH;
    PDMA_REQSEL_HSYNC   = state0;
state0:
    TIMER_HSYNC->CMP    = LINE_ACTIVE;      // trigger after LINE_ACTIVE pixels
    disp_buff_cur       = PDMA_PIXEL->SA;
    PDMA_PIXEL->CTL     = PIXEL_ACTIVE;     // send PIXEL_ACTIVE pixels
    PDMA_REQSEL_HSYNC   = state1;           // same request from TIMER_HSYNC, different PDMA channel to trigger different PDMA operation
    PDMA_HSYNC->CTL     = 1;                // trigger once
state1:
    TIMER_HSYNC->CMP    = HSYNC + PORCH;
    PDMA_REQSEL_HSYNC   = state2;
state2:
    PDMA_PIXEL->SA      = disp_buff_cur;    // repeat previous line buffer to divide the screen height by 2(n = 2)
    PDMA_PIXEL->CTL     = PIXEL_ACTIVE;     // send PIXEL_ACTIVE pixels
    PDMA_REQSEL_HSYNC   = state3;           // same request from TIMER_HSYNC, different PDMA channel to trigger different PDMA operation
    PDMA_HSYNC->CTL     = 1;                // trigger once
state3:
    TIMER_HSYNC->CMP    = HSYNC + PORCH;
    PDMA_REQSEL_HSYNC   = state0;

vsync(uint line): similar to hsync
*/

/*============================ MACROS ========================================*/

#define VGA_M480_CFG_X_DIV              4
#define VGA_M480_CFG_Y_DIV              4

#define VGA_M480_CFG_PIXEL_CLK          (36 * 1000 * 1000)

#define VGA_M480_CFG_H_SYNC_PIXELS      72
#define VGA_M480_CFG_H_FPORCH_PIXELS    24
#define VGA_M480_CFG_H_ACTIVE_PIXELS    800
#define VGA_M480_CFG_H_BPORCH_PIXELS    128

#define VGA_M480_CFG_V_SYNC_LINES       2
#define VGA_M480_CFG_V_FPORCH_LINES     1
#define VGA_M480_CFG_V_ACTIVE_LINES     600
#define VGA_M480_CFG_V_BPORCH_LINES     22




#define VGA_M480_PIXEL_CLK              (VGA_M480_CFG_PIXEL_CLK / VGA_M480_CFG_X_DIV)

#define VGA_M480_H_SYNC_PIXELS          (VGA_M480_CFG_H_SYNC_PIXELS / VGA_M480_CFG_X_DIV)
#define VGA_M480_H_FPORCH_PIXELS        (VGA_M480_CFG_H_FPORCH_PIXELS / VGA_M480_CFG_X_DIV)
#define VGA_M480_H_ACTIVE_PIXELS        (VGA_M480_CFG_H_ACTIVE_PIXELS / VGA_M480_CFG_X_DIV)
#define VGA_M480_H_BPORCH_PIXELS        (VGA_M480_CFG_H_BPORCH_PIXELS / VGA_M480_CFG_X_DIV)
#define VGA_M480_H_PIXELS               (VGA_M480_H_SYNC_PIXELS + VGA_M480_H_FPORCH_PIXELS + VGA_M480_H_ACTIVE_PIXELS + VGA_M480_H_BPORCH_PIXELS)

#define VGA_M480_V_SYNC_LINES           VGA_M480_CFG_V_SYNC_LINES
#define VGA_M480_V_FPORCH_LINES         VGA_M480_CFG_V_FPORCH_LINES
#define VGA_M480_V_ACTIVE_LINES         VGA_M480_CFG_V_ACTIVE_LINES
#define VGA_M480_V_BPORCH_LINES         VGA_M480_CFG_V_BPORCH_LINES
#define VGA_M480_V_LINES                (VGA_M480_V_SYNC_LINES + VGA_M480_V_FPORCH_LINES + VGA_M480_V_ACTIVE_LINES + VGA_M480_V_BPORCH_LINES)

/*============================ MACROFIED FUNCTIONS ===========================*/
/*============================ TYPES =========================================*/

enum {
    VSF_EVT_REFRESH = VSF_EVT_USER,
};

typedef struct m480_pdma_desc_t {
    uint32_t ctl;
    uint32_t src;
    uint32_t dest;
    uint32_t offset;
} m480_pdma_desc_t;

typedef struct vga_m480_t {
    struct {
        struct {
            uint32_t    pulse_bporch;
            uint32_t    active_fporch;
        } h;
        struct {
            uint32_t    fporch_pulse_bporch;
            uint32_t    active;
        } v;
    } param;

    uint8_t             *frame_buffer;
    uint32_t            reg_dma_ctl;
    uint32_t            pixel_trg_enable;
    uint32_t            pixel_trg_disable;
    uint32_t            pixel_trg_control;
    uint32_t            pixel_dma_enable;

    uint32_t            hsync_desc_state[2];
    uint32_t            hsync_reqsrc_state[2];
    m480_pdma_desc_t    hsync_state0[16];
    m480_pdma_desc_t    hsync_state1[16];

    uint32_t            vsync_desc_state[2];
    uint32_t            vsync_reqsrc_state[2];
    m480_pdma_desc_t    vsync_state0[16];
    m480_pdma_desc_t    vsync_state1[16];

    // TODO: over-clocking to 288M hz will meed more line pixels to make it stable, duno why
    uint8_t             vga_buffer[VGA_M480_V_ACTIVE_LINES / VGA_M480_CFG_Y_DIV][320];
} vga_m480_t;

/*============================ LOCAL VARIABLES ===============================*/
/*============================ PROTOTYPES ====================================*/

static vsf_err_t __vk_disp_vga_m480_init(vk_disp_t *pthis);
static vsf_err_t __vk_disp_vga_m480_refresh(vk_disp_t *pthis, vk_disp_area_t *area, void *disp_buff);

/*============================ GLOBAL VARIABLES ==============================*/

const vk_disp_drv_t vk_disp_drv_vga_m480 = {
    .init           = __vk_disp_vga_m480_init,
    .refresh        = __vk_disp_vga_m480_refresh,
};

static vga_m480_t __vga_m480 = {
    .param              = {
        .h.pulse_bporch = (VGA_M480_H_SYNC_PIXELS + VGA_M480_H_BPORCH_PIXELS - 13) * (M480_PCLK0_FREQ_HZ / VGA_M480_PIXEL_CLK) - 1,
        .h.active_fporch= (VGA_M480_H_ACTIVE_PIXELS + VGA_M480_H_FPORCH_PIXELS + 13) * (M480_PCLK0_FREQ_HZ / VGA_M480_PIXEL_CLK) - 1,
        .v.fporch_pulse_bporch = (VGA_M480_V_FPORCH_LINES + VGA_M480_V_SYNC_LINES + VGA_M480_V_BPORCH_LINES) * VGA_M480_H_PIXELS * (M480_PCLK0_FREQ_HZ / VGA_M480_PIXEL_CLK) - 1,
        .v.active       = (VGA_M480_V_ACTIVE_LINES) * VGA_M480_H_PIXELS * (M480_PCLK0_FREQ_HZ / VGA_M480_PIXEL_CLK) - 1,
    },

    .frame_buffer       = (uint8_t *)__vga_m480.vga_buffer,
    .reg_dma_ctl        = (2 << PDMA_DSCT_CTL_OPMODE_Pos),
    .pixel_trg_enable   = (0 << TIMER_TRGCTL_TRGSSEL_Pos) | TIMER_TRGCTL_TRGEPWM_Msk | TIMER_TRGCTL_TRGPDMA_Msk,
    .pixel_trg_disable  = (0 << TIMER_TRGCTL_TRGSSEL_Pos) | TIMER_TRGCTL_TRGEPWM_Msk,
    .pixel_trg_control  = (0 << TIMER_TRGCTL_TRGSSEL_Pos) | TIMER_TRGCTL_TRGEPWM_Msk,
    .pixel_dma_enable   = (VGA_M480_H_ACTIVE_PIXELS << PDMA_DSCT_CTL_TXCNT_Pos) | (3 << PDMA_DSCT_CTL_DAINC_Pos) | PDMA_DSCT_CTL_TBINTDIS_Msk | (1 << PDMA_DSCT_CTL_TXTYPE_Pos) | (1 << PDMA_DSCT_CTL_OPMODE_Pos),

    .hsync_reqsrc_state = {
        (47 << PDMA_REQSEL12_15_REQSRC12_Pos),
        (47 << PDMA_REQSEL12_15_REQSRC13_Pos),
    },
    .vsync_reqsrc_state = {
        (46 << PDMA_REQSEL8_11_REQSRC9_Pos) | (48 << PDMA_REQSEL8_11_REQSRC10_Pos),
        (46 << PDMA_REQSEL8_11_REQSRC9_Pos) | (48 << PDMA_REQSEL8_11_REQSRC11_Pos),
    },
};

/*============================ IMPLEMENTATION ================================*/

// TODO: use dma api in hal
void PDMA_IRQHandler(void)
{
    if (PDMA->TDSTS & (1 << 13)) {
        static int cnt = 0;
        if (cnt++ >= VGA_M480_CFG_Y_DIV) {
            PDMA->DSCT[9].SA += sizeof(__vga_m480.vga_buffer[0]);
            cnt = 0;
        }
        PDMA->TDSTS = 1 << 13;
    }
}

static void __vk_disp_vga_m480_hw_init(void)
{
    // PB0..7 pixel output
    PB->DATMSK          = ~0x00FF;
    PB->DOUT            = 0;
    PB->MODE            = 0x5555;
    SYS->GPB_MFPL       = 0;
    // PB12.15 TMx_EXT
    SYS->GPB_MFPH       = (11 << SYS_GPB_MFPH_PB15MFP_Pos) | (13 << SYS_GPB_MFPH_PB14MFP_Pos) | (13 << SYS_GPB_MFPH_PB13MFP_Pos) | (11 << SYS_GPB_MFPH_PB12MFP_Pos);
    SYS->GPB_MFOS       = 0;

    // TIMER0/TIMER1/TIMER2 clock
    CLK->APBCLK0        |= CLK_APBCLK0_TMR0CKEN_Msk | CLK_APBCLK0_TMR1CKEN_Msk | CLK_APBCLK0_TMR2CKEN_Msk | CLK_APBCLK0_TMR3CKEN_Msk;
    CLK->CLKSEL1        &= ~(CLK_CLKSEL1_TMR0SEL_Msk | CLK_CLKSEL1_TMR1SEL_Msk | CLK_CLKSEL1_TMR2SEL_Msk | CLK_CLKSEL1_TMR3SEL_Msk);
    CLK->CLKSEL1        |= (2 << CLK_CLKSEL1_TMR0SEL_Pos) | (2 << CLK_CLKSEL1_TMR1SEL_Pos) | (2 << CLK_CLKSEL1_TMR2SEL_Pos) | (2 << CLK_CLKSEL1_TMR3SEL_Pos);

    // TIMER0 for div_pixel_clk
    TIMER0->CTL         = TIMER_CTL_ICEDEBUG_Msk | (1 << TIMER_CTL_OPMODE_Pos) | (1 << TIMER_CTL_TGLPINSEL_Pos);
    TIMER0->CMP         = M480_PCLK0_FREQ_HZ / VGA_M480_PIXEL_CLK;
    TIMER0->TRGCTL      = __vga_m480.pixel_trg_disable;
    // TIMER1 for hsync_trg
    TIMER1->CTL         = TIMER_CTL_ICEDEBUG_Msk | (2 << TIMER_CTL_OPMODE_Pos) | TIMER_CTL_PERIOSEL_Msk | (1 << TIMER_CTL_TGLPINSEL_Pos);
    TIMER1->CMP         = __vga_m480.param.h.pulse_bporch;
    TIMER1->TRGCTL      = (0 << TIMER_TRGCTL_TRGSSEL_Pos) | TIMER_TRGCTL_TRGPDMA_Msk;

    CLK->SyncCLK         |= CLK_SyncCLK_PDMACKEN_Msk;
    PDMA->SCATBA        = (uint32_t)&__vga_m480 & 0xFFFF0000;
    PDMA->INTEN         |= (1 << 13);
    NVIC_SetPriority(PDMA_IRQn, vsf_arch_prio_highest);
    NVIC_EnableIRQ(PDMA_IRQn);

    // PDMA channel 9 for pixel output
    PDMA->DSCT[9].CTL   = 0;
    PDMA->DSCT[9].SA    = (uint32_t)__vga_m480.frame_buffer;
    PDMA->DSCT[9].DA    = (uint32_t)&PB->DOUT;
    PDMA->PRISET        |= (1 << 9);
    PDMA->CHCTL         |= (1 << 9);

    // PDMA channel 12-15 for hsync_trg state0-3
    PDMA->REQSEL12_15   = __vga_m480.hsync_reqsrc_state[0];
    __vga_m480.hsync_desc_state[0] = (uint32_t)__vga_m480.hsync_state0 - PDMA->SCATBA;
    __vga_m480.hsync_desc_state[1] = (uint32_t)__vga_m480.hsync_state1 - PDMA->SCATBA;
    m480_pdma_desc_t *desc;
    // state0:
    desc = __vga_m480.hsync_state0;
    PDMA->DSCT[12].CTL  = __vga_m480.reg_dma_ctl;
    PDMA->DSCT[12].NEXT = (uint32_t)desc - PDMA->SCATBA;
    desc[0].ctl         = (0 << PDMA_DSCT_CTL_TXCNT_Pos) | (2 << PDMA_DSCT_CTL_TXWIDTH_Pos) | (3 << PDMA_DSCT_CTL_DAINC_Pos) | (3 << PDMA_DSCT_CTL_SAINC_Pos) | PDMA_DSCT_CTL_TBINTDIS_Msk | (7 << PDMA_DSCT_CTL_BURSIZE_Pos) | (2 << PDMA_DSCT_CTL_OPMODE_Pos);
    desc[0].src         = (uint32_t)&__vga_m480.param.h.active_fporch;
    desc[0].dest        = (uint32_t)&TIMER1->CMP;
    desc[1].ctl         = (0 << PDMA_DSCT_CTL_TXCNT_Pos) | (2 << PDMA_DSCT_CTL_TXWIDTH_Pos) | (3 << PDMA_DSCT_CTL_DAINC_Pos) | (3 << PDMA_DSCT_CTL_SAINC_Pos) | PDMA_DSCT_CTL_TBINTDIS_Msk | (7 << PDMA_DSCT_CTL_BURSIZE_Pos) | (2 << PDMA_DSCT_CTL_OPMODE_Pos);
    desc[1].src         = (uint32_t)&__vga_m480.pixel_trg_control;
    desc[1].dest        = (uint32_t)&TIMER0->TRGCTL;
    desc[2].ctl         = (0 << PDMA_DSCT_CTL_TXCNT_Pos) | (2 << PDMA_DSCT_CTL_TXWIDTH_Pos) | (3 << PDMA_DSCT_CTL_DAINC_Pos) | (3 << PDMA_DSCT_CTL_SAINC_Pos) | PDMA_DSCT_CTL_TBINTDIS_Msk | (7 << PDMA_DSCT_CTL_BURSIZE_Pos) | (2 << PDMA_DSCT_CTL_OPMODE_Pos);
    desc[2].src         = (uint32_t)&__vga_m480.pixel_dma_enable;
    desc[2].dest        = (uint32_t)&PDMA->DSCT[9].CTL;
    desc[3].ctl         = (0 << PDMA_DSCT_CTL_TXCNT_Pos) | (2 << PDMA_DSCT_CTL_TXWIDTH_Pos) | (3 << PDMA_DSCT_CTL_DAINC_Pos) | (3 << PDMA_DSCT_CTL_SAINC_Pos) | PDMA_DSCT_CTL_TBINTDIS_Msk | (7 << PDMA_DSCT_CTL_BURSIZE_Pos) | (2 << PDMA_DSCT_CTL_OPMODE_Pos);
    desc[3].src         = (uint32_t)&__vga_m480.hsync_reqsrc_state[1];
    desc[3].dest        = (uint32_t)&PDMA->REQSEL12_15;
    desc[4].ctl         = (0 << PDMA_DSCT_CTL_TXCNT_Pos) | (2 << PDMA_DSCT_CTL_TXWIDTH_Pos) | (3 << PDMA_DSCT_CTL_DAINC_Pos) | (3 << PDMA_DSCT_CTL_SAINC_Pos) | PDMA_DSCT_CTL_TBINTDIS_Msk | (7 << PDMA_DSCT_CTL_BURSIZE_Pos) | (2 << PDMA_DSCT_CTL_OPMODE_Pos);
    desc[4].src         = (uint32_t)&__vga_m480.reg_dma_ctl;
    desc[4].dest        = (uint32_t)&PDMA->DSCT[13].CTL;
    desc[5].ctl         = (0 << PDMA_DSCT_CTL_TXCNT_Pos) | (2 << PDMA_DSCT_CTL_TXWIDTH_Pos) | (3 << PDMA_DSCT_CTL_DAINC_Pos) | (3 << PDMA_DSCT_CTL_SAINC_Pos) | PDMA_DSCT_CTL_TBINTDIS_Msk | (7 << PDMA_DSCT_CTL_BURSIZE_Pos) | (2 << PDMA_DSCT_CTL_OPMODE_Pos);
    desc[5].src         = (uint32_t)&__vga_m480.hsync_desc_state[1];
    desc[5].dest        = (uint32_t)&PDMA->DSCT[13].NEXT;
    desc[0].offset      = (uint32_t)&desc[1] - PDMA->SCATBA;
    desc[1].offset      = (uint32_t)&desc[2] - PDMA->SCATBA;
    desc[2].offset      = (uint32_t)&desc[3] - PDMA->SCATBA;
    desc[3].offset      = (uint32_t)&desc[4] - PDMA->SCATBA;
    desc[4].offset      = (uint32_t)&desc[5] - PDMA->SCATBA;
    desc[5].offset      = 0;
    // state1:
    desc = __vga_m480.hsync_state1;
    desc[0].ctl         = (0 << PDMA_DSCT_CTL_TXCNT_Pos) | (2 << PDMA_DSCT_CTL_TXWIDTH_Pos) | (3 << PDMA_DSCT_CTL_DAINC_Pos) | (3 << PDMA_DSCT_CTL_SAINC_Pos) | PDMA_DSCT_CTL_TBINTDIS_Msk | (7 << PDMA_DSCT_CTL_BURSIZE_Pos) | (2 << PDMA_DSCT_CTL_OPMODE_Pos);
    desc[0].src         = (uint32_t)&__vga_m480.param.h.pulse_bporch;
    desc[0].dest        = (uint32_t)&TIMER1->CMP;
    desc[1].ctl         = (0 << PDMA_DSCT_CTL_TXCNT_Pos) | (2 << PDMA_DSCT_CTL_TXWIDTH_Pos) | (3 << PDMA_DSCT_CTL_DAINC_Pos) | (3 << PDMA_DSCT_CTL_SAINC_Pos) | PDMA_DSCT_CTL_TBINTDIS_Msk | (7 << PDMA_DSCT_CTL_BURSIZE_Pos) | (2 << PDMA_DSCT_CTL_OPMODE_Pos);
    desc[1].src         = (uint32_t)&__vga_m480.hsync_reqsrc_state[0];
    desc[1].dest        = (uint32_t)&PDMA->REQSEL12_15;
    desc[2].ctl         = (0 << PDMA_DSCT_CTL_TXCNT_Pos) | (2 << PDMA_DSCT_CTL_TXWIDTH_Pos) | (3 << PDMA_DSCT_CTL_DAINC_Pos) | (3 << PDMA_DSCT_CTL_SAINC_Pos) | PDMA_DSCT_CTL_TBINTDIS_Msk | (7 << PDMA_DSCT_CTL_BURSIZE_Pos) | (2 << PDMA_DSCT_CTL_OPMODE_Pos);
    desc[2].src         = (uint32_t)&__vga_m480.reg_dma_ctl;
    desc[2].dest        = (uint32_t)&PDMA->DSCT[12].CTL;
    desc[3].ctl         = (0 << PDMA_DSCT_CTL_TXCNT_Pos) | (2 << PDMA_DSCT_CTL_TXWIDTH_Pos) | (3 << PDMA_DSCT_CTL_DAINC_Pos) | (3 << PDMA_DSCT_CTL_SAINC_Pos) | (7 << PDMA_DSCT_CTL_BURSIZE_Pos) | (2 << PDMA_DSCT_CTL_OPMODE_Pos);
    desc[3].src         = (uint32_t)&__vga_m480.hsync_desc_state[0];
    desc[3].dest        = (uint32_t)&PDMA->DSCT[12].NEXT;
    desc[0].offset      = (uint32_t)&desc[1] - PDMA->SCATBA;
    desc[1].offset      = (uint32_t)&desc[2] - PDMA->SCATBA;
    desc[2].offset      = (uint32_t)&desc[3] - PDMA->SCATBA;
    desc[3].offset      = 0;
    PDMA->PRISET        |= (1 << 12) | (1 << 13);
    PDMA->CHCTL         |= (1 << 12) | (1 << 13);

    // TIMER2 for vsync_trg
    TIMER2->CTL         = TIMER_CTL_ICEDEBUG_Msk | (2 << TIMER_CTL_OPMODE_Pos) | TIMER_CTL_PERIOSEL_Msk | (1 << TIMER_CTL_TGLPINSEL_Pos);
    TIMER2->CMP         = (VGA_M480_V_LINES - VGA_M480_V_FPORCH_LINES + 2) * VGA_M480_H_PIXELS * (M480_PCLK0_FREQ_HZ / VGA_M480_PIXEL_CLK) - 1;
    TIMER2->TRGCTL      = (0 << TIMER_TRGCTL_TRGSSEL_Pos) | TIMER_TRGCTL_TRGPDMA_Msk;

    // PDMA channel 10-11 for vsync_trg state0-1
    PDMA->REQSEL8_11   = __vga_m480.vsync_reqsrc_state[0];
    __vga_m480.vsync_desc_state[0] = (uint32_t)__vga_m480.vsync_state0 - PDMA->SCATBA;
    __vga_m480.vsync_desc_state[1] = (uint32_t)__vga_m480.vsync_state1 - PDMA->SCATBA;
    // state0:
    desc = __vga_m480.vsync_state0;
    PDMA->DSCT[10].CTL  = __vga_m480.reg_dma_ctl;
    PDMA->DSCT[10].NEXT = (uint32_t)desc - PDMA->SCATBA;
    desc[0].ctl         = (0 << PDMA_DSCT_CTL_TXCNT_Pos) | (2 << PDMA_DSCT_CTL_TXWIDTH_Pos) | (3 << PDMA_DSCT_CTL_DAINC_Pos) | (3 << PDMA_DSCT_CTL_SAINC_Pos) | PDMA_DSCT_CTL_TBINTDIS_Msk | (7 << PDMA_DSCT_CTL_BURSIZE_Pos) | (2 << PDMA_DSCT_CTL_OPMODE_Pos);
    desc[0].src         = (uint32_t)&__vga_m480.pixel_trg_disable;
    desc[0].dest        = (uint32_t)&TIMER0->TRGCTL;
    desc[1].ctl         = (0 << PDMA_DSCT_CTL_TXCNT_Pos) | (2 << PDMA_DSCT_CTL_TXWIDTH_Pos) | (3 << PDMA_DSCT_CTL_DAINC_Pos) | (3 << PDMA_DSCT_CTL_SAINC_Pos) | PDMA_DSCT_CTL_TBINTDIS_Msk | (7 << PDMA_DSCT_CTL_BURSIZE_Pos) | (2 << PDMA_DSCT_CTL_OPMODE_Pos);
    desc[1].src         = (uint32_t)&__vga_m480.pixel_trg_disable;
    desc[1].dest        = (uint32_t)&__vga_m480.pixel_trg_control;
    desc[2].ctl         = (0 << PDMA_DSCT_CTL_TXCNT_Pos) | (2 << PDMA_DSCT_CTL_TXWIDTH_Pos) | (3 << PDMA_DSCT_CTL_DAINC_Pos) | (3 << PDMA_DSCT_CTL_SAINC_Pos) | PDMA_DSCT_CTL_TBINTDIS_Msk | (7 << PDMA_DSCT_CTL_BURSIZE_Pos) | (2 << PDMA_DSCT_CTL_OPMODE_Pos);
    desc[2].src         = (uint32_t)&__vga_m480.param.v.fporch_pulse_bporch;
    desc[2].dest        = (uint32_t)&TIMER2->CMP;
    desc[3].ctl         = (0 << PDMA_DSCT_CTL_TXCNT_Pos) | (2 << PDMA_DSCT_CTL_TXWIDTH_Pos) | (3 << PDMA_DSCT_CTL_DAINC_Pos) | (3 << PDMA_DSCT_CTL_SAINC_Pos) | PDMA_DSCT_CTL_TBINTDIS_Msk | (7 << PDMA_DSCT_CTL_BURSIZE_Pos) | (2 << PDMA_DSCT_CTL_OPMODE_Pos);
    desc[3].src         = (uint32_t)&__vga_m480.vsync_reqsrc_state[1];
    desc[3].dest        = (uint32_t)&PDMA->REQSEL8_11;
    desc[4].ctl         = (0 << PDMA_DSCT_CTL_TXCNT_Pos) | (2 << PDMA_DSCT_CTL_TXWIDTH_Pos) | (3 << PDMA_DSCT_CTL_DAINC_Pos) | (3 << PDMA_DSCT_CTL_SAINC_Pos) | PDMA_DSCT_CTL_TBINTDIS_Msk | (7 << PDMA_DSCT_CTL_BURSIZE_Pos) | (2 << PDMA_DSCT_CTL_OPMODE_Pos);
    desc[4].src         = (uint32_t)&__vga_m480.reg_dma_ctl;
    desc[4].dest        = (uint32_t)&PDMA->DSCT[11].CTL;
    desc[5].ctl         = (0 << PDMA_DSCT_CTL_TXCNT_Pos) | (2 << PDMA_DSCT_CTL_TXWIDTH_Pos) | (3 << PDMA_DSCT_CTL_DAINC_Pos) | (3 << PDMA_DSCT_CTL_SAINC_Pos) | PDMA_DSCT_CTL_TBINTDIS_Msk | (7 << PDMA_DSCT_CTL_BURSIZE_Pos) | (2 << PDMA_DSCT_CTL_OPMODE_Pos);
    desc[5].src         = (uint32_t)&__vga_m480.vsync_desc_state[1];
    desc[5].dest        = (uint32_t)&PDMA->DSCT[11].NEXT;
    desc[0].offset      = (uint32_t)&desc[1] - PDMA->SCATBA;
    desc[1].offset      = (uint32_t)&desc[2] - PDMA->SCATBA;
    desc[2].offset      = (uint32_t)&desc[3] - PDMA->SCATBA;
    desc[3].offset      = (uint32_t)&desc[4] - PDMA->SCATBA;
    desc[4].offset      = (uint32_t)&desc[5] - PDMA->SCATBA;
    desc[5].offset      = 0;
    // state1:
    desc = __vga_m480.vsync_state1;
    desc[0].ctl         = (0 << PDMA_DSCT_CTL_TXCNT_Pos) | (2 << PDMA_DSCT_CTL_TXWIDTH_Pos) | (3 << PDMA_DSCT_CTL_DAINC_Pos) | (3 << PDMA_DSCT_CTL_SAINC_Pos) | PDMA_DSCT_CTL_TBINTDIS_Msk | (7 << PDMA_DSCT_CTL_BURSIZE_Pos) | (2 << PDMA_DSCT_CTL_OPMODE_Pos);
    desc[0].src         = (uint32_t)&__vga_m480.frame_buffer;
    desc[0].dest        = (uint32_t)&PDMA->DSCT[9].SA;
    desc[1].ctl         = (0 << PDMA_DSCT_CTL_TXCNT_Pos) | (2 << PDMA_DSCT_CTL_TXWIDTH_Pos) | (3 << PDMA_DSCT_CTL_DAINC_Pos) | (3 << PDMA_DSCT_CTL_SAINC_Pos) | PDMA_DSCT_CTL_TBINTDIS_Msk | (7 << PDMA_DSCT_CTL_BURSIZE_Pos) | (2 << PDMA_DSCT_CTL_OPMODE_Pos);
    desc[1].src         = (uint32_t)&__vga_m480.pixel_trg_enable;
    desc[1].dest        = (uint32_t)&__vga_m480.pixel_trg_control;
    desc[2].ctl         = (0 << PDMA_DSCT_CTL_TXCNT_Pos) | (2 << PDMA_DSCT_CTL_TXWIDTH_Pos) | (3 << PDMA_DSCT_CTL_DAINC_Pos) | (3 << PDMA_DSCT_CTL_SAINC_Pos) | PDMA_DSCT_CTL_TBINTDIS_Msk | (7 << PDMA_DSCT_CTL_BURSIZE_Pos) | (2 << PDMA_DSCT_CTL_OPMODE_Pos);
    desc[2].src         = (uint32_t)&__vga_m480.hsync_reqsrc_state[0];
    desc[2].dest        = (uint32_t)&PDMA->REQSEL12_15;
    desc[3].ctl         = (0 << PDMA_DSCT_CTL_TXCNT_Pos) | (2 << PDMA_DSCT_CTL_TXWIDTH_Pos) | (3 << PDMA_DSCT_CTL_DAINC_Pos) | (3 << PDMA_DSCT_CTL_SAINC_Pos) | PDMA_DSCT_CTL_TBINTDIS_Msk | (7 << PDMA_DSCT_CTL_BURSIZE_Pos) | (2 << PDMA_DSCT_CTL_OPMODE_Pos);
    desc[3].src         = (uint32_t)&__vga_m480.reg_dma_ctl;
    desc[3].dest        = (uint32_t)&PDMA->DSCT[12].CTL;
    desc[4].ctl         = (0 << PDMA_DSCT_CTL_TXCNT_Pos) | (2 << PDMA_DSCT_CTL_TXWIDTH_Pos) | (3 << PDMA_DSCT_CTL_DAINC_Pos) | (3 << PDMA_DSCT_CTL_SAINC_Pos) | PDMA_DSCT_CTL_TBINTDIS_Msk | (7 << PDMA_DSCT_CTL_BURSIZE_Pos) | (2 << PDMA_DSCT_CTL_OPMODE_Pos);
    desc[4].src         = (uint32_t)&__vga_m480.hsync_desc_state[0];
    desc[4].dest        = (uint32_t)&PDMA->DSCT[12].NEXT;
    desc[5].ctl         = (0 << PDMA_DSCT_CTL_TXCNT_Pos) | (2 << PDMA_DSCT_CTL_TXWIDTH_Pos) | (3 << PDMA_DSCT_CTL_DAINC_Pos) | (3 << PDMA_DSCT_CTL_SAINC_Pos) | PDMA_DSCT_CTL_TBINTDIS_Msk | (7 << PDMA_DSCT_CTL_BURSIZE_Pos) | (2 << PDMA_DSCT_CTL_OPMODE_Pos);
    desc[5].src         = (uint32_t)&__vga_m480.param.v.active;
    desc[5].dest        = (uint32_t)&TIMER2->CMP;
    desc[6].ctl         = (0 << PDMA_DSCT_CTL_TXCNT_Pos) | (2 << PDMA_DSCT_CTL_TXWIDTH_Pos) | (3 << PDMA_DSCT_CTL_DAINC_Pos) | (3 << PDMA_DSCT_CTL_SAINC_Pos) | PDMA_DSCT_CTL_TBINTDIS_Msk | (7 << PDMA_DSCT_CTL_BURSIZE_Pos) | (2 << PDMA_DSCT_CTL_OPMODE_Pos);
    desc[6].src         = (uint32_t)&__vga_m480.vsync_reqsrc_state[0];
    desc[6].dest        = (uint32_t)&PDMA->REQSEL8_11;
    desc[7].ctl         = (0 << PDMA_DSCT_CTL_TXCNT_Pos) | (2 << PDMA_DSCT_CTL_TXWIDTH_Pos) | (3 << PDMA_DSCT_CTL_DAINC_Pos) | (3 << PDMA_DSCT_CTL_SAINC_Pos) | PDMA_DSCT_CTL_TBINTDIS_Msk | (7 << PDMA_DSCT_CTL_BURSIZE_Pos) | (2 << PDMA_DSCT_CTL_OPMODE_Pos);
    desc[7].src         = (uint32_t)&__vga_m480.reg_dma_ctl;
    desc[7].dest        = (uint32_t)&PDMA->DSCT[10].CTL;
    desc[8].ctl         = (0 << PDMA_DSCT_CTL_TXCNT_Pos) | (2 << PDMA_DSCT_CTL_TXWIDTH_Pos) | (3 << PDMA_DSCT_CTL_DAINC_Pos) | (3 << PDMA_DSCT_CTL_SAINC_Pos) | PDMA_DSCT_CTL_TBINTDIS_Msk | (7 << PDMA_DSCT_CTL_BURSIZE_Pos) | (2 << PDMA_DSCT_CTL_OPMODE_Pos);
    desc[8].src         = (uint32_t)&__vga_m480.vsync_desc_state[0];
    desc[8].dest        = (uint32_t)&PDMA->DSCT[10].NEXT;
    desc[0].offset      = (uint32_t)&desc[1] - PDMA->SCATBA;
    desc[1].offset      = (uint32_t)&desc[2] - PDMA->SCATBA;
    desc[2].offset      = (uint32_t)&desc[3] - PDMA->SCATBA;
    desc[3].offset      = (uint32_t)&desc[4] - PDMA->SCATBA;
    desc[4].offset      = (uint32_t)&desc[5] - PDMA->SCATBA;
    desc[5].offset      = (uint32_t)&desc[6] - PDMA->SCATBA;
    desc[6].offset      = (uint32_t)&desc[7] - PDMA->SCATBA;
    desc[7].offset      = (uint32_t)&desc[8] - PDMA->SCATBA;
    desc[8].offset      = 0;
    PDMA->PRISET        |= (1 << 10) | (1 << 11);
    PDMA->CHCTL         |= (1 << 10) | (1 << 11);

    // EPWM for vsync and hsync
    CLK->APBCLK1        |= CLK_APBCLK1_EPWM1CKEN_Msk;
    CLK->CLKSEL2        &= ~(CLK_CLKSEL2_EPWM1SEL_Msk);
    CLK->CLKSEL2        |= (0 << CLK_CLKSEL2_EPWM1SEL_Pos);
    EPWM1->CTL0         = EPWM_CTL0_IMMLDEN0_Msk | EPWM_CTL0_IMMLDEN3_Msk;
    EPWM1->CTL1         = 0;
    EPWM1->CLKSRC       &= ~(EPWM_CLKSRC_ECLKSRC0_Msk | EPWM_CLKSRC_ECLKSRC2_Msk);
    EPWM1->CLKSRC       |= (1 << EPWM_CLKSRC_ECLKSRC0_Pos) | (1 << EPWM_CLKSRC_ECLKSRC2_Pos);
    EPWM1->CLKPSC[0]    = VGA_M480_H_PIXELS - 1;
    EPWM1->CLKPSC[1]    = 0;
    EPWM1->CNTCLR       |= EPWM_CNTCLR_CNTCLR0_Msk | EPWM_CNTCLR_CNTCLR3_Msk;
    EPWM1->PERIOD[0]    = VGA_M480_V_LINES - 1;
    EPWM1->CMPDAT[0]    = VGA_M480_V_SYNC_LINES;
    EPWM1->PERIOD[3]    = VGA_M480_H_PIXELS - 1;
    EPWM1->CMPDAT[3]    = VGA_M480_H_SYNC_PIXELS;
    EPWM1->WGCTL0       = (1 << EPWM_WGCTL0_ZPCTL0_Pos) | (1 << EPWM_WGCTL0_ZPCTL3_Pos);
    EPWM1->WGCTL1       = (2 << EPWM_WGCTL1_CMPUCTL0_Pos) | (2 << EPWM_WGCTL1_CMPUCTL3_Pos);
    EPWM1->POEN         |= EPWM_POEN_POEN0_Msk | EPWM_POEN_POEN3_Msk;
    EPWM1->CNTEN        |= EPWM_CNTEN_CNTEN0_Msk | EPWM_CNTEN_CNTEN3_Msk;

    vsf_protect_t orig = vsf_protect_int();
//        PDMA->SWREQ     = 1 << 10;
//        PDMA->SWREQ     = 1 << 11;
//        PDMA->SWREQ     = 1 << 12;
//        PDMA->SWREQ     = 1 << 13;
//        PDMA->SWREQ     = 1 << 14;
//        PDMA->SWREQ     = 1 << 15;
//        PDMA->SWREQ     = 1 << 9;
        TIMER0->CTL     |= TIMER_CTL_CNTEN_Msk;
        TIMER1->CTL     |= TIMER_CTL_CNTEN_Msk;
        TIMER2->CTL     |= TIMER_CTL_CNTEN_Msk;
//        while (1);
    vsf_unprotect_int(orig);
}

static void __vk_disp_vga_m480_evthandler(vsf_eda_t *eda, vsf_evt_t evt)
{
    vk_disp_vga_m480_t *disp_vga = vsf_container_of(eda, vk_disp_vga_m480_t, eda);

    switch (evt) {
    case VSF_EVT_REFRESH:
        break;
    }
}

static vsf_err_t __vk_disp_vga_m480_init(vk_disp_t *pthis)
{
    vk_disp_vga_m480_t *disp_vga = (vk_disp_vga_m480_t *)pthis;
    VSF_UI_ASSERT(disp_vga != NULL);

    // memory init for test, remove later
    for (int i = 0; i < dimof(__vga_m480.vga_buffer); i++) {
        for (int j = 0; j < dimof(__vga_m480.vga_buffer[0]); j++) {
            if (i & 1) {
                if (j < 100) {
                    if (j & 1) {
                        __vga_m480.vga_buffer[i][j] = 0x03;
                    }
                } else if (j < 130) {
                    if (!(j & 1)) {
                        __vga_m480.vga_buffer[i][j] = 0x1C;
                    }
                } else if (j < VGA_M480_H_ACTIVE_PIXELS + 1) {
                    if (j & 1) {
                        __vga_m480.vga_buffer[i][j] = 0xE0;
                    }
                } else {
                    __vga_m480.vga_buffer[i][j] = 0x00;
                }
            }
        }
    }
    __vk_disp_vga_m480_hw_init();

    disp_vga->eda.fn.evthandler = __vk_disp_vga_m480_evthandler;
#if VSF_KERNEL_CFG_EDA_SUPPORT_ON_TERMINATE == ENABLED
    disp_vga->eda.on_terminate = NULL;
#endif
    return vsf_eda_init(&disp_vga->eda);
}

static vsf_err_t __vk_disp_vga_m480_refresh(vk_disp_t *pthis, vk_disp_area_t *area, void *disp_buff)
{
    vk_disp_vga_m480_t *disp_vga = (vk_disp_vga_m480_t *)pthis;
    VSF_UI_ASSERT(disp_vga != NULL);

    disp_vga->disp_buff = disp_buff;
    disp_vga->area      = *area;
    vsf_eda_post_evt(&disp_vga->eda, VSF_EVT_REFRESH);
    return VSF_ERR_NONE;
}

#endif

/* EOF */
